package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.lkd.common.VMSystem;

import com.lkd.config.ConsulConfig;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;

import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutData;
import com.lkd.dao.VendingMachineDao;

import com.lkd.emq.MqttProducer;
import com.lkd.entity.*;
import com.lkd.exception.LogicException;
import com.lkd.http.vo.CreateVMReq;
import com.lkd.service.*;

import com.lkd.utils.DistributedLock;
import com.lkd.utils.UUIDUtils;
import com.lkd.vo.Pager;
import com.lkd.vo.SkuVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class VendingMachineServiceImpl extends ServiceImpl<VendingMachineDao, VendingMachineEntity> implements VendingMachineService {

    @Autowired
    private NodeService nodeService;

    @Autowired
    private ChannelService channelService;

    @Autowired
    private VmTypeService vmTypeService;
    @Autowired
    private MqttProducer mqttProducer;
    @Autowired
    VendoutRunningService vendoutRunningService;
    @Autowired
    ConsulConfig consulConfig;
    @Autowired
    StringRedisTemplate redisTemplate;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean add(CreateVMReq vendingMachine) {
        VendingMachineEntity vendingMachineEntity = new VendingMachineEntity();
        vendingMachineEntity.setNodeId(Long.valueOf(vendingMachine.getNodeId()));
        vendingMachineEntity.setVmType(vendingMachine.getVmType());
        NodeEntity nodeEntity = nodeService.getById(vendingMachine.getNodeId());
        if (nodeEntity == null) {
            throw new LogicException("所选点位不存在");
        }
        //复制属性
        BeanUtils.copyProperties(nodeEntity, vendingMachineEntity);
        vendingMachineEntity.setCreateUserId(Long.valueOf(vendingMachine.getCreateUserId()));
        vendingMachineEntity.setInnerCode(UUIDUtils.getUUID());
        vendingMachineEntity.setClientId(UUIDUtils.generateClientId(vendingMachineEntity.getInnerCode()));
        this.save(vendingMachineEntity);
        //创建货道数据
        createChannel(vendingMachineEntity);
        return true;
    }


    /**
     * 创建货道
     *
     * @param vm
     * @return
     */
    private boolean createChannel(VendingMachineEntity vm) {
        VmTypeEntity vmType = vmTypeService.getById(vm.getVmType());
        List<ChannelEntity> channelList = Lists.newArrayList();
        for (int i = 1; i <= vmType.getVmRow(); i++) {
            for (int j = 1; j <= vmType.getVmCol(); j++) {
                ChannelEntity channel = new ChannelEntity();
                channel.setChannelCode(i + "-" + j);
                channel.setCurrentCapacity(0);
                channel.setInnerCode(vm.getInnerCode());
                channel.setLastSupplyTime(vm.getLastSupplyTime());
                channel.setMaxCapacity(vmType.getChannelMaxCapacity());
                channel.setVmId(vm.getId());
                channelList.add(channel);
            }
        }
        channelService.saveBatch(channelList);
        return true;
    }


    @Override
    public boolean update(Long id, Long nodeId) {
        VendingMachineEntity vm = this.getById(id);
        if (vm.getVmStatus() == VMSystem.VM_STATUS_RUNNING)
            throw new LogicException("改设备正在运营");
        NodeEntity nodeEntity = nodeService.getById(nodeId);
        BeanUtils.copyProperties(nodeEntity, vm);
        return this.updateById(vm);
    }


    @Override
    public Pager<String> getAllInnerCodes(boolean isRunning, long pageIndex, long pageSize) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<VendingMachineEntity> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageIndex, pageSize);

        QueryWrapper<VendingMachineEntity> qw = new QueryWrapper<>();
        if (isRunning) {
            qw.lambda()
                    .select(VendingMachineEntity::getInnerCode)
                    .eq(VendingMachineEntity::getVmStatus, 1);
        } else {
            qw.lambda()
                    .select(VendingMachineEntity::getInnerCode)
                    .ne(VendingMachineEntity::getVmStatus, 1);
        }
        this.page(page, qw);
        Pager<String> result = new Pager<>();
        result.setCurrentPageRecords(page.getRecords().stream().map(VendingMachineEntity::getInnerCode).collect(Collectors.toList()));
        result.setPageIndex(page.getCurrent());
        result.setPageSize(page.getSize());
        result.setTotalCount(page.getTotal());

        return result;
    }

    @Override
    public Pager<VendingMachineEntity> query(Long pageIndex, Long pageSize, Integer status, String innerCode) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<VendingMachineEntity> page
                = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageIndex, pageSize);
        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        if (status != null) {
            queryWrapper.eq(VendingMachineEntity::getVmStatus, status);
        }
        if (!Strings.isNullOrEmpty(innerCode)) {
            queryWrapper.likeLeft(VendingMachineEntity::getInnerCode, innerCode);
        }
        this.page(page, queryWrapper);

        return Pager.build(page);
    }


    @Override
    public VmVO findByInnerCode(String innerCode) {
        LambdaQueryWrapper<VendingMachineEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VendingMachineEntity::getInnerCode, innerCode);
        VendingMachineEntity vm = this.getOne(queryWrapper);
        VmVO vmVO = new VmVO();
        BeanUtils.copyProperties(vm, vmVO);
        vmVO.setNodeAddr(vm.getNode().getAddr());//地址
        vmVO.setNodeName(vm.getNode().getName());//名称
        return vmVO;
    }


    @Override
    public boolean updateStatus(String innerCode, Integer status) {
        try {
            UpdateWrapper<VendingMachineEntity> uw = new UpdateWrapper<>();
            uw.lambda()
                    .eq(VendingMachineEntity::getInnerCode, innerCode)
                    .set(VendingMachineEntity::getVmStatus, status);
            this.update(uw);

        } catch (Exception ex) {
            log.error("updateStatus error,innerCode is " + innerCode + " status is " + status, ex);
            return false;
        }
        return true;
    }


    @Override
    public List<SkuVO> getSkuListByInnerCode(String innerCode) {
        //获取货道列表
        List<ChannelEntity> channelList = channelService.getChannelesByInnerCode(innerCode).stream()
                .filter(c -> c.getSkuId() > 0 && c.getSku() != null).collect(Collectors.toList());
        //获取有商品的库存余量
        Map<SkuEntity, Integer> skuMap = channelList.stream()
                .collect(Collectors.groupingBy(
                        ChannelEntity::getSku,
                        Collectors.summingInt(ChannelEntity::getCurrentCapacity)));//对库存数求和
        return skuMap.entrySet().stream().map(entry -> {
                    SkuEntity sku = entry.getKey(); //查询商品
                    SkuVO skuVO = new SkuVO();
                    BeanUtils.copyProperties(sku, skuVO);
                    skuVO.setImage(sku.getSkuImage());//图片
                    skuVO.setCapacity(entry.getValue());
                    skuVO.setRealPrice(sku.getPrice());//真实价格
                    return skuVO;
                }).sorted(Comparator.comparing(SkuVO::getCapacity).reversed())  //按库存量降序排序
                .collect(Collectors.toList());
    }

    @Override
    public Boolean hasCapacity(String innerCode, Long skuId) {
        var qw = new LambdaQueryWrapper<ChannelEntity>();
        qw
                .eq(ChannelEntity::getInnerCode, innerCode)
                .eq(ChannelEntity::getSkuId, skuId)
                .gt(ChannelEntity::getCurrentCapacity, 0);
        return channelService.count(qw) > 0;
    }

    @Override
    public VendingMachineEntity findByClientId(String clientId) {
        QueryWrapper<VendingMachineEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(VendingMachineEntity::getClientId, clientId);
        return this.getOne(qw);
    }

    @Autowired
    VendingMachineDao vendingMachineDao;

    @Override
    public void supply(String innerCode, List<SupplyChannel> supplyChannelList) {
        //根据机器码查询当前工单机器的货道
        List<ChannelEntity> channelEntityList = channelService.getChannelesByInnerCode(innerCode);
        //判断当前机器货道是否存在
        if (channelEntityList == null) {
            throw new LogicException("当前机器货道不存在");
        }
        //获得当前机器货道的所有的channelcode货道编号
        //List<String> channelCodeList = channelEntityList.stream().map(ChannelEntity::getChannelCode).collect(Collectors.toList());
        //List<String> supplyCodeList = supplyChannelList.stream().map(SupplyChannel::getChannelId).collect(Collectors.toList());
        /*channelEntityList.forEach(channelEntity -> {
            supplySku1(channelEntity,supplyCodeList);
        });*/
        //对需要补货的货道进行遍历
        supplyChannelList.forEach(supplyChannel -> {
            //对需要补货的货道进行补货
            supplySku(supplyChannel, channelEntityList);
        });
        //修改完补货后的信息  保存
        /* vendingMachineDao.updateChannelSkuBySupply(channelEntityList,innerCode);*/

    }

    private void supplySku(SupplyChannel supplyChannel, List<ChannelEntity> channelEntityList) {
        //遍历当前售货机的货道 对需要补货的货道进行补货
        channelEntityList.forEach(channelEntity -> {
            String channelCode = supplyChannel.getChannelId();
            if (channelCode.equals(channelEntity.getChannelCode())) {
                //当前货道容量设置
                channelEntity.setCurrentCapacity(channelEntity.getCurrentCapacity() + supplyChannel.getCapacity());
                //当前商品的id
                channelEntity.setSkuId(supplyChannel.getSkuId());
                //当前
                channelEntity.setLastSupplyTime(LocalDateTime.now());
                channelService.updateById(channelEntity);
            }
        });
    }

    /**
     * 自动创建售货机补货工单
     *
     * @param code
     */
    @Override
    public void autoSupplySkuTask(String code) {
        //1.创建协议封装类
        SupplyContract supplyContract = new SupplyContract();
        List<SupplyChannel> supplyChannelList = new ArrayList<>();
        supplyContract.setInnerCode(code);
        //2.获取当前售货机的对应的所有货道
        //List<ChannelEntity> channelEntityList = channelService.getChannelesByInnerCode(code);
        List<ChannelEntity> channelEntityList = channelService.list(Wrappers.<ChannelEntity>lambdaQuery()
                .eq(ChannelEntity::getInnerCode, code)
                .ne(ChannelEntity::getSkuId, 0));
        if (channelEntityList == null) {
            log.info("货道未设置");
            return;
        }
        //3.对应货道商品进行判断是否需要补货.l
        channelEntityList.forEach(channelEntity -> {
            if (channelEntity.getMaxCapacity() < channelEntity.getCurrentCapacity()) {
                throw new LogicException("库存不能大于最大容量");
            }
            if (channelEntity.getMaxCapacity() > channelEntity.getCurrentCapacity()) {
                //3.1需要补货的数量
                int numForSupply = channelEntity.getMaxCapacity() - channelEntity.getCurrentCapacity();
                //3.2该货道需要补货==>补货数量 封装
                SupplyChannel supplyChannel = new SupplyChannel();
                supplyChannel.setChannelId(String.valueOf(channelEntity.getChannelCode()));
                supplyChannel.setCapacity(numForSupply);
                supplyChannel.setSkuId(channelEntity.getSkuId());
                supplyChannel.setSkuName(channelEntity.getSku().getSkuName());
                supplyChannel.setSkuImage(channelEntity.getSku().getSkuImage());
                supplyChannelList.add(supplyChannel);
            }
        });

        supplyContract.setSupplyData(supplyChannelList);
        if (CollectionUtils.isEmpty(supplyChannelList)) return;
        //使用EMQ发送协议
        try {
            log.info("发送补货工单成功");
            mqttProducer.send(TopicConfig.TASK_SUPPLY_TOPIC, 2, supplyContract);
        } catch (JsonProcessingException e) {
            log.info("发送补货工单失败");
            e.printStackTrace();
        }
    }

    @Override
    public Boolean vendOutSku(VendoutContract contract) {
        try {
            //1.根据售货机码获得货道信息
            List<ChannelEntity> channeles = channelService.getChannelesByInnerCode(contract.getInnerCode());
            //2.获取订单商品数据信息
            VendoutData data = contract.getVendoutData();
            //3.stream筛选货道
            Optional<ChannelEntity> channel = channeles.stream().filter(channelEntity -> channelEntity.getCurrentCapacity() > 0)
                    .filter(channelEntity -> data.getSkuId() == channelEntity.getSkuId()).findFirst();
            //3.1判断满足参数的条件是否存在
            if (channel.isPresent()) {//存在
                ChannelEntity channelEntity = channel.get();
                //库存减一
                channelEntity.setCurrentCapacity(channelEntity.getCurrentCapacity() - 1);
                channelService.updateById(channelEntity);
                //3.2封装出货流水数据
                VendoutRunningEntity vendoutRunning = new VendoutRunningEntity();
                vendoutRunning.setOrderNo(data.getOrderNo());
                vendoutRunning.setInnerCode(contract.getInnerCode());
                vendoutRunning.setStatus(false);
                vendoutRunning.setSkuId(channelEntity.getSkuId());
                vendoutRunning.setSkuName(channelEntity.getSku().getSkuName());
                vendoutRunning.setPrice(channelEntity.getPrice());
                vendoutRunning.setCreateTime(LocalDateTime.now());
                vendoutRunning.setUpdateTime(LocalDateTime.now());
                //3.3保存出货信息
                vendoutRunningService.save(vendoutRunning);
                //4 向对应的售货机发送出货的消息

                mqttProducer.send(TopicConfig.getVendoutTopic(contract.getInnerCode()), 2, contract);
                //TODO:----------------解锁售货机的状态
                DistributedLock lock = new DistributedLock(
                        consulConfig.getConsulRegisterHost(),
                        consulConfig.getConsulRegisterPort());
                //拼接对应遁入redis中的key
                String sessionId =  redisTemplate.boundValueOps(
                        VMSystem.VM_LOCK_KEY_PREF + contract.getInnerCode()
                                + "_" + contract.getVendoutData().getSkuId()).get();
                //释放锁
                lock.releaseLock(sessionId);
                return true;
            } else {
                log.info("缺货开始退款");
                return false;
            }
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return false;
        }
    }
}
